#include "config.h"

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>

#include "xsp-proto.h"

#include "libxsp_client.h"

#define LGET_BUF_SIZE 1048576

struct sockaddr_in *nameport2sa(const char *name_port);

int main(int argc, char *argv[])
{
	extern char *optarg;
	extern int errno;
	extern int optind;
	int c;
	int output_fd = -1;
	int debug = 0;
	int socket_bufsize = 0;
	static char usage[] = "usage: lget [-V] [-d debug_level] [-b buffer/window size] ip/port [ip/port]\n";
	char *buf;
	uint64_t total=0;
	int recvd;
	struct timeval stime, etime;
	double ttime, rate;
	int i;
	char *output_file = NULL;
	libxspSess *sess;

	while((c = getopt(argc, argv, "o:b:d:i:t:V")) != -1) {
		switch(c) {
			case 'V':
				printf("Logistical Session File Getter, version 0.2.1.\n");
				printf("%s\n", usage);
				exit(1);
				break;

			case 'b':
				socket_bufsize = atoi(optarg);
				break;

			case 'd':
				debug = atoi(optarg);
				if (debug < 1) debug = 1;
				break;

			case 'o':
				output_file = strdup(optarg);
				break;

			default:
				fprintf(stderr, usage); 
				exit(1);
		}
	}

	if (output_file) {
		output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
		if (output_fd < 0) {
			perror("Couldn't open output file\n");
			exit(errno);
		}
	}

	if (libxsp_init() < 0) {
		perror("libxsp_init(): failed");
		exit(errno);
	}

	sess = xsp_session();
	if (!sess) {
		perror("xsp_session() failed");
		exit(errno);
	}

	if ((buf = (char *)malloc(LGET_BUF_SIZE)) == NULL) {
		printf("malloc of %dK failed\n", LGET_BUF_SIZE / 1024);
		exit(-1);
	}

	if(optind == argc) {
		fprintf(stderr, usage);
		exit(0);
	}

	for(i=optind; i < (argc - 1); i++) {
		if(debug > 0) {
			printf("calling nameport2sa on '%s'\n", argv[i]);
		}

		xsp_sess_appendchild(sess, argv[i], XSP_HOP_NATIVE);
	}

	if (socket_bufsize > 0) {
		xsp_setsockopt(sess, XSP_SOCKET, SO_RCVBUF, (const void *)&socket_bufsize,
				sizeof(int));
		xsp_setsockopt(sess, XSP_SOCKET, SO_SNDBUF, (const void *)&socket_bufsize,
				sizeof(int));
	}

	if (debug > 0 && socket_bufsize > 0) {
		int set_socket_bufsize = 0;
		int size = sizeof(int);

		if((xsp_getsockopt(sess, XSP_SOCKET, SO_RCVBUF,
						(char *)&set_socket_bufsize, (socklen_t *)&size)) < 0) {
			perror("lget: getsockopt(SO_RCVBUF) failed");
		}
		else {
			printf("lget: SO_RCVBUF set to %d\n", set_socket_bufsize);
		}
		set_socket_bufsize = 0;
		if((xsp_getsockopt(sess, XSP_SOCKET, SO_SNDBUF,
						(char *)&set_socket_bufsize, (socklen_t *) &size)) < 0) {
			perror("lget: getsockopt(SO_SNDBUF) failed");
		}
		else {
			printf("lget: SO_SNDBUF set to %d\n", set_socket_bufsize);
		}
	}

	xsp_sess_appendchild(sess, argv[argc - 1], 0);

	/* argc - 1 is the ultimate dest */
	if (xsp_connect(sess)) {
		perror("lget: connect failed");
		exit(errno);
	}

	recvd=1;

	gettimeofday(&stime, NULL);

	while(recvd > 0) {

		recvd = xsp_recv(sess, buf, LGET_BUF_SIZE, MSG_WAITALL);
		total += recvd;

		if (recvd < 0) {
			perror("error in recv\n");
		}

		if (output_fd > 0)
			write(output_fd, buf, recvd);

		if (debug > 5) {
			printf("recvd: %d\n", recvd);
		}
	}

	xsp_close(sess);

	gettimeofday(&etime, NULL);

	stime.tv_usec += (stime.tv_sec * 1000000);
	etime.tv_usec += (etime.tv_sec * 1000000);

	ttime = (etime.tv_usec -= stime.tv_usec);

	ttime = ttime / 1000000;

	rate = (((double)(total / ttime)) / 1048576);

	if(debug > 0) {
		printf("%lld bytes received in %f seconds at %f MBytes/sec (%f Mb/sec)\n", \
				total, ttime, rate, (rate*8));
	} 

	printf("%lld\t%f\n", total, (rate*8)); 

	return 0;
}
